package com.gf.framework.redis.aspect;

import com.gf.framework.common.exception.BizException;
import com.gf.framework.common.result.BaseResultCodeEnum;
import com.gf.framework.redis.core.RedisClient;
import com.gf.framework.redis.annotation.RepeatRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author gaofei
 * @Description 防重入切片
 * @create 2022/5/24 13:44
 */
@Slf4j
@Aspect
@Order(100)
@Component
public class RepeatRequestAspect extends RedissonBaseAspect{
    @Autowired
    RedisClient redisClient;

    @Around("@annotation(com.gf.framework.redis.annotation.RepeatRequest)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        RepeatRequest repeatRequest = method.getAnnotation(RepeatRequest.class);

        String key = getKey(repeatRequest.key(), repeatRequest.value(), joinPoint);
        if (repeatRequest.refusetime() > 0) {
            long time = repeatRequest.timeUnit().
                    toMillis(repeatRequest.refusetime());
            return refuseLock(key, joinPoint, time, repeatRequest.deleteKey());
        }

        return repeatLock(key, joinPoint);
    }

    private Object refuseLock(
            String key,ProceedingJoinPoint joinPoint,
            long time, boolean deleteKey) throws Throwable{
        long nowtime = System.currentTimeMillis();
        if (!redisClient.setNx(key, "1", time, TimeUnit.MILLISECONDS)) {
            // 加锁失败自动拒绝
            throw new BizException(BaseResultCodeEnum.REPETITIVE_OPERATION.getCode(),
                    BaseResultCodeEnum.REPETITIVE_OPERATION.getMessage());
        }

        if (!deleteKey) {
            return joinPoint.proceed();
        }
        else {
            try {
                return joinPoint.proceed();
            }catch (Throwable throwable) {
                long outTime = System.currentTimeMillis();
                if (outTime - nowtime > time) {
                    // 处理失败后，把key回收
                    redisClient.delete(key);
                }
                throw  throwable;
            }
        }
    }


    private Object repeatLock(String key, ProceedingJoinPoint joinPoint) throws Throwable{
        RLock rLock = redissonClient.getLock(key);
        if (rLock == null || !rLock.tryLock()) {
            throw new BizException(BaseResultCodeEnum.REPETITIVE_OPERATION.getCode(),
                    BaseResultCodeEnum.REPETITIVE_OPERATION.getMessage());
        }
        try {
            return joinPoint.proceed();
        }
        catch (Throwable throwable) {
            throw throwable;
        }
        finally {
            rLock.unlock();
        }
    }
}
